<!DOCTYPE html>
<html lang="zh-cn">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">




<title>Web上传 | Miles`s</title>

<link rel="stylesheet" href="https://mada.gitee.io/notes//css/styles.css">

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" 
integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>


<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/zenburn.min.css" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js" integrity="sha256-/BfiIkHlHoVihZdc6TFuj7MmJ0TWcWsMXkeDFwhi0zw=" crossorigin="anonymous"></script>
<script>hljs.initHighlightingOnLoad();</script>
<script src="https://mada.gitee.io/notes//js/highlight.js"></script>






<div class="container">
    <nav class="navbar level">
      <div class="navbar-brand">
          <a class="nav-item" href="https://mada.gitee.io/notes/"><h1 class="title is-3">Miles`s</h1></a>
      </div>           
      <div class="navbar-menu has-text-centered is-active">
          <div class="navbar-end is-centered">
              
           </div>
      </div>
    </nav>
  </div>
<div class="container">
  <h2 class="subtitle is-6">November 28, 2016</h2>
  <h1 class="subtitle is-size-4-mobile is-size-3-desktop">Web上传</h1>
  <div class="content">
    

<p>web上传的实现方式</p>

<h2 id="表单提交上传">表单提交上传</h2>

<h3 id="普通表单提交与上传表单提交区别">普通表单提交与上传表单提交区别</h3>

<p><strong>普通表单</strong></p>

<pre><code class="language-html">&lt;form action=&quot;/&quot; &gt;
	&lt;input type=&quot;text&quot; name=&quot;name&quot;&gt;&lt;/input&gt;
	&lt;input type=&quot;text&quot; name=&quot;age&quot;&gt;&lt;/input&gt;
	&lt;button type=&quot;submit&quot;&gt;确定&lt;/button&gt;
&lt;/form&gt;
</code></pre>

<p><strong>上传表单</strong></p>

<pre><code class="language-html">&lt;form action=&quot;/upload&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;
	&lt;input type=&quot;file&quot; accept=&quot;images/*&quot;&gt;&lt;/input&gt;
	&lt;button type=&quot;submit&quot;&gt;上传&lt;/button&gt;
&lt;/form&gt;
</code></pre>

<p>上传表单中，对<code>form</code>标签的<code>enctype=&quot;multipart/form-data&quot;</code>。
而普通的表单提交，一般不用去定义该属性，存在个磨人的值<code>x-www-form-urlencoded</code>。</p>

<p>对input标签type属性添加了新的值file，也为input标签添加了一个新的属性accept来过滤上传文件的类型。</p>

<p>同时也定义了一个新的MIME media type：multipart/form-data,在form标签中定义enctype=&ldquo;multipart/form-data&rdquo;。</p>

<p>在点击提交时，两者的请求头与请求体也有所不同。</p>

<p><strong>普通表单提交</strong></p>

<pre><code>#request header
Content-type : x-www-form-urlencoded;setchart:UTF-8

#request body
form-data
name:xxx
age:111
</code></pre>

<p><strong>上传表单提交</strong></p>

<pre><code>#request header
Content-type : multipart/form-data; boundary=----WebKitFormBoundaryK8NOU66bOhWkzidB

#request body
------WebKitFormBoundaryK8NOU66bOhWkzidB
Content-Disposition: form-data; name=&quot;resume&quot;; filename=&quot;test-text-file.txt&quot;
Content-Type: text/plain
Test text file for testing file uploads.
------WebKitFormBoundaryK8NOU66bOhWkzidB--
</code></pre>

<p>上传表单提交，除了在Content-type中改变了MIME type,同时在其后添加了个属性boundary，用于表示提交的表单中各个字段的边界分割，不同浏览器生产的这个边界字符串是不同的。如上例子中的&mdash;-WebKitFormBoundaryK8NOU66bOhWkzidB也只是使用webkit内核的才会出现&mdash;-WebKitForm。</p>

<p>表单上传提交的优缺点</p>

<p>对现在的web上传来说，那时候的表单上传没什么优点，非要说优点，那就是简单，简单到不需要些一行JS代码。</p>

<p>说缺点的话，那就是一箩筐了。
上传文件会页面跳转，不能选择多文件上传（想要上传多文件，就必须有多个input），不能限制上传大小，不能获得上传进度信息，不能预览，等等所有现在的上传需要都无法满足。</p>

<p>PS:
HTML5中对<input type="file">添加了个新属性multiple，使之能够选择多文件，从而让表单上传提交变得方便。但话说，都有HTML5了，还需要用这种页面跳转的方式做上传吗？</p>

<p>HTML5也添加了文件对象(File)，可以方便的获得文件本身的一些信息，name,size,type，在上传前知道文件的大小，实现限制上传大小；</p>

<h2 id="iframe表单上传提交">iframe表单上传提交</h2>

<p>是表单上传提交的优化版，本质上也是表单上传提交，一般都是通过创建个隐藏的iframe来提交表单，以此达到页面不跳转上传。</p>

<p><strong>html代码</strong></p>

<pre><code class="language-html">&lt;form method=&quot;POST&quot; action=&quot;/iframeUpload&quot; id=&quot;my_form&quot; enctype=&quot;multipart/form-data&quot; &gt;
    &lt;div class=&quot;form-group&quot;&gt;
       	&lt;a href=&quot;javascript:;&quot; class=&quot;btn btn-default chooseFile&quot;&gt;选择文件
          	&lt;input type=&quot;file&quot; name=&quot;inputFile&quot;   id=&quot;inputFile&quot; /&gt;&lt;/a&gt;
        &lt;button  id=&quot;submit_btn&quot; type=&quot;submit&quot; onclick=&quot;fileUpload();&quot;&gt;Upload&lt;/button&gt;
    &lt;/div&gt;
&lt;/form&gt;
``

**javascript代码**

```js
function eventHandler(){
	//...get data ,or do somthing..
}

function fileUpload(){
	var form = document.getElementById('my_form');
	var iframe = document.createElement('iframe');
	iframe.setAttribute(&quot;id&quot;, &quot;upload_iframe&quot;);
    iframe.setAttribute(&quot;name&quot;, &quot;upload_iframe&quot;);
	iframe.setAttribute(&quot;width&quot;, &quot;0&quot;);
	iframe.setAttribute(&quot;height&quot;, &quot;0&quot;);
	iframe.setAttribute(&quot;border&quot;, &quot;0&quot;);
	iframe.setAttribute(&quot;style&quot;, &quot;width: 0; height: 0; border: none;&quot;);
	form.parentNode.appendChild(iframe);
	iframeId.addEventListener(&quot;load&quot;, eventHandler, true);
	iframeId.attachEvent(&quot;onload&quot;, eventHandler);
	form.submit();
}
</code></pre>

<p>可表单上传提交的其他缺点还是存在。</p>

<p>还是不能获得上传进度信息，想要获得上传进度信息，需要服务端的支持，轮询服务端接口。</p>

<p>并且iframe无法实现跨域上传</p>

<p>所以总体来说，iframe更适合上传单个文件，并上传的文件较小（文件较大 ，没有进度条信息的话，用户体验太差）。</p>

<p><strong>flash上传</strong></p>

<p>在HTML5之前，最流行的上传方式，能实现的功能也较多。</p>

<p><strong>flash上传的优点：</strong></p>

<ol>
<li>支持上传进度条</li>
<li>支持预览（图片等）</li>
<li>支持多文件上传</li>
<li>支持图片压缩</li>
<li>支持分片上传</li>
<li>支持暂停上传</li>
<li>支持秒传</li>
</ol>

<p><strong>flash上传的缺点</strong></p>

<ol>
<li>需要浏览器安装flash插件</li>
<li>网站需要加载相应的swf文件</li>
<li>对移动端不兼容（iOS一直不支持，Android5.0之后也不支持）</li>
</ol>

<p><strong>flash跨域上传</strong></p>

<p>如果网站的地址，与上传请求的地址不是同域，则会存在flash数据传输跨域问题。</p>

<p>需要在请求地址的服务端根目录下添加crossdomain.xml文件</p>

<p>文件内容如下：</p>

<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;cross-domain-policy&gt;
	&lt;allow-access-from doamin=&quot;*&quot;/&gt;
&lt;/cross-domain-prolicy&gt;
</code></pre>

<h2 id="h5-xhr2-xmlhttprequest-level-2-上传">H5—xhr2（XmlHttpRequest level 2）上传</h2>

<p>xhr即我们常说的ajax(Asynchronous JavaScript and XML)</p>

<p><strong>xhr2的特点</strong></p>

<ul>
<li>可以设置HTTP请求的时限。</li>
<li>可以获取服务器端的（或向服务端发送）二进制数据。</li>
<li>可以使用FormData对象管理表单数据。</li>
<li>可以上传文件。xhr.upload(upload = XMLHttpRequestUpload)</li>
<li>可以获得数据传输的进度信息, xhr.upload.onprogess。</li>
<li>可以请求不同域名下的数据（跨域请求）。</li>
</ul>

<p><strong>主要javascript代码</strong></p>

<pre><code class="language-js">
function uploadFile(file){
   var xhr = new XMLHttpRequest();
   xhr.open('POST','/upload',true);
   var formData = new FormData();
   xhr.upload.onprogress = function(data){
      var per = Math.ceil((data.loaded/data.total)*100);
      //$('#'+file.uid+' .progress-bar').css('width',per+'%');
   }
   xhr.onreadystatechange = function() {
       if (xhr.readyState == 4 &amp;&amp; xhr.status == 200) {
         // Every thing ok, file uploaded
           var res = JSON.parse(xhr.responseText);
           if(res.code ==200){
               // upload success
           }
       }
   };
   formData.append(&quot;upload_file&quot;, file);
   formData.append(&quot;filename&quot;,file.name);
   xhr.send(formData);
}

</code></pre>

<p>xhr2在结合H5的其他特性，可以实现上述flash上传的所以功能外，还可以实现拖拽上传功能。</p>

<p>由于诸多HTML5特性（Blob ,xhr2,FileReader,ArrayBuffer等）在IE10+中才有效，
所以xhr2上传更适合在chrome，firefox等高版本的浏览器或和移动端使用。</p>

<h2 id="cors-跨域资源共享">CORS(跨域资源共享)</h2>

<p>允许一个域上的网络应用向另一个域提交跨域 AJAX 请求。启用此功能非常简单，只需由服务器发送一个响应标头即可。例如：
Access-Control-Allow-Origin: <a href="http://example.com">http://example.com</a></p>

<p>当然还有其他的一些配置，比如配置允许的自定义头，允许的请求方式等等。这里不作详细说明，具体内容可查看这里</p>

<p>这图清晰的整个跨域资源共享的请求过程。</p>

<h2 id="h5-websocket上传">H5—websocket上传</h2>

<pre><code>var url = &quot;ws://localhost:8081/upload&quot;;
var ws = new WebSocket(url);
ws.binaryType = 'arraybuffer';
ws.onopen = function () {
    window.console.log('websocket connection success ...');
};

//...
ws.onerror = function (error) {
  window.console.log('WebSocket Error ' + error);
};

//...
function uploadFile(file){
   //实例化FileReader对象
   var fr = new FileReader();
   //定义文件加载完的监听事件，执行回调函数 
   fr.addEventListener(&quot;loadend&quot;, function() {
      ws.send(fr.result);
   });
   //把文件加载进ArrayBuffer中
   fr.readAsArrayBuffer(file);
}
</code></pre>

<p>实际使用中，浏览器websocket用做上传较少</p>

<p>websocket上传存在几个问题：</p>

<ol>
<li>一般对于现有的上传服务，服务端需要单独开发接口</li>
<li>同样无法获得上传的进度信息（变通方式：必须使用分片来模拟进度）</li>
</ol>

<h1 id="todo">TODO:</h1>

<ul>
<li>断点续传</li>
<li>后台实现</li>
</ul>

<h2 id="简单比较几种上传组件">简单比较几种上传组件</h2>

<p><a href="https://blueimp.github.io/jQuery-File-Upload/">jQuery-File-Upload</a></p>

<p>在github上的人气相当的高，是上述所说iframe与xhr2上传的结合，介绍说支持IE6以上浏览器的上传功能。</p>

<p>此话也不假，iframe用的就是form表单提交上传，确实可以在IE7，IE8等浏览器中完成上传。
但也就是上传，上传相关的其他”特效功能”都实现不了。所以在我看来，jQuery-File-Uplaod更适合用于chrome ,firefox等或移动端的上传。</p>

<p><a href="http://www.uploadify.com/">Uploadify</a></p>

<p>是个基于swfupload的上传组件，同时也支持xhr2上传。是falsh上传与xhr2上传的结合，同样的功能强大。对浏览器的兼容要比jQuery-File-Upload要更好</p>

<p><a href="https://github.com/fex-team/webuploader">fex tema webuploader</a></p>

<p>与Uploadify一样是falsh上传与xhr2上传的结合，但从查看两者的API会发现，webUploader实现的功能要更多，更强大。 比如：分片上传，MD5秒传，图片压缩上传.</p>

<p>From: <a href="http://www.devsai.com/2016/11/08/talk-web-upload/">SAI</a></p>

  </div>
</div>
<div class="container has-text-centered">
    
</div>

<div class="container has-text-centered">
  
</div>
<section class="section">
  <div class="container has-text-centered">
    <p></p>
  </div>
</section>


